"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
const git_utils_1 = require("../utils/git-utils");
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("path"));
const child_process = __importStar(require("child_process"));
const bootstrap = (options = {}) => __awaiter(void 0, void 0, void 0, function* () {
    const {
    // eslint-disable-next-line no-shadowed-variable
    onlyBuildMicro, onlyBuildParent, buildDll, packages,
    // buildMicroWithoutParent,
    concurrentBuildNumber = 0, } = options;
    process.env.BABEL_ENV = 'production';
    process.env.NODE_ENV = 'production';
    const { envData } = require('../config/env');
    const paths = require('../config/paths');
    const { setCachedEnv } = require('../utils/cache');
    const compileEnvFile = path.resolve(paths.appConfig, 'compileBuildEnv.js'); // config 下 compileBuildEnv.js
    if (fs.existsSync(compileEnvFile)) {
        Object.assign(process.env, require(compileEnvFile));
    }
    const SKIP_NO_CHANGE_MODULE = process.env.SKIP_NO_CHANGE_MODULE === 'true';
    const buildDistPath = paths.appRootBuild; // 默认是dist
    // try {
    //   if (
    //     (await envIsChanged()) &&
    //     !process.env.BUILD_MODULE &&
    //     SKIP_NO_CHANGE_MODULE &&
    //     !onlyBuildParent
    //   ) {
    //     console.log('warning: 环境变量发生改变，需要重新生成 dll 和 dist');
    //     fs.emptyDirSync(buildDistPath);
    //     if (fs.existsSync(paths.appDll)) {
    //       fs.emptyDirSync(paths.appDll);
    //       fs.rmdirSync(paths.appDll, { recursive: true });
    //     }
    //   }
    // } catch (e) {
    //   console.error(e);
    // }
    // Ensure environment variables are read.
    const chalk = require('react-dev-utils/chalk');
    const webpack = require('webpack');
    const configFactory = require('../config/webpack.config');
    const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
    const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
    const printHostingInstructions = require('react-dev-utils/printHostingInstructions');
    const FileSizeReporter = require('react-dev-utils/FileSizeReporter');
    // Do this as the first thing so that any code reading it knows the right env.
    // Makes the script crash on unhandled rejections instead of silently
    // ignoring them. In the future, promise rejections that are not handled will
    // terminate the Node.js process with a non-zero exit code.
    process.on('unhandledRejection', (err) => {
        throw err;
    });
    const measureFileSizesBeforeBuild = FileSizeReporter.measureFileSizesBeforeBuild; // 捕获dist 目录下 JS和CSS资产大小
    const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild; // 打印文件size  在 build 之后
    const useYarn = fs.existsSync(paths.yarnLockFile); // 是否使用 yarn
    // These sizes are pretty large. We'll warn for bundles exceeding them.
    const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024;
    const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024;
    const isInteractive = process.stdout.isTTY; // 是否文本终端环境 tty.ReadStream tty.WriteStream
    const DISABLE_BUILD_DLL = process.env.DISABLE_BUILD_DLL === 'true';
    // // 编译之前先检查 common 模块是否已经编译
    const transpileModules = yield require('./transpile').transpileCommonIfChange();
    if (transpileModules.length) {
        fs.emptyDirSync(paths.appParentBuild);
    }
    // Warn and crash if required files are missing
    if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
        process.exit(1);
    } // cpaas-boot/lib/pathInfo 不存在 index 则结束进程 正常会继续进行编译
    if (!DISABLE_BUILD_DLL) {
        if (buildDll) {
            yield require('./build-dll').default();
            return;
        }
    }
    // Create the production build and print the deployment instructions.
    function build(previousFileSizes) {
        console.log('Creating an optimized production build...');
        // Generate configuration
        const config = configFactory('production');
        let compiler = webpack(config);
        return new Promise((resolve, reject) => {
            compiler.run((err, stats) => {
                let messages;
                if (err) {
                    if (!err.message) {
                        return reject(err);
                    }
                    messages = formatWebpackMessages({
                        errors: [err.message],
                        warnings: [],
                    });
                }
                else {
                    messages = formatWebpackMessages(stats.toJson({
                        all: false,
                        warnings: true,
                        errors: true,
                    }));
                }
                if (messages.errors.length) {
                    // Only keep the first error. Others are often indicative
                    // of the same problem, but confuse the reader with noise.
                    if (messages.errors.length > 1) {
                        messages.errors.length = 1;
                    }
                    return reject(new Error(messages.errors.join('\n\n')));
                }
                if (process.env.CI &&
                    (typeof process.env.CI !== 'string' ||
                        process.env.CI.toLowerCase() !== 'false') &&
                    messages.warnings.length) {
                    console.log(chalk.yellow('\nTreating warnings as errors because process.env.CI = true.\n' +
                        'Most CI servers set it automatically.\n'));
                    console.warn(messages.warnings.join('\n\n'));
                    // return reject(new Error(messages.warnings.join('\n\n')));
                }
                const resolveArgs = {
                    stats,
                    previousFileSizes,
                    warnings: messages.warnings,
                    config,
                };
                return resolve(resolveArgs);
            });
        });
    }
    /**
     * copy cpaas-boot 下 public 到 dist
     */
    function copyPublicFolder() {
        fs.copySync(paths.cpaasBootPublic, buildDistPath, {
            dereference: true,
            filter: (file) => file !== paths.appHtml,
        });
        if (fs.existsSync(paths.appPublic)) {
            fs.copySync(paths.appPublic, buildDistPath, {
                dereference: true,
                filter: (file) => file !== paths.appHtml,
            });
        }
        if (!DISABLE_BUILD_DLL && fs.existsSync(paths.appDll)) {
            fs.copySync(paths.appDll, buildDistPath, {
                dereference: true,
                filter: (file) => file !== paths.appHtml,
            });
        }
        if (fs.existsSync(paths.appParentBuild)) {
            fs.copySync(paths.appParentBuild, buildDistPath, {
                dereference: true,
                filter: (file) => file !== paths.appHtml,
            });
        }
    }
    /**
     * 编译 cpaas 子模块
     */
    function buildCpaasMicroModules() {
        return __awaiter(this, void 0, void 0, function* () {
            let buildPackageNames = [];
            // 如果 cwd 是子模块的目录，就只编译当前模块.
            if (paths.appRootPath !== paths.appPath) {
                buildPackageNames = [
                    fs.readJSONSync(path.resolve(paths.appPath, 'package.json')).name,
                ]; // 只编译子模块
            }
            else {
                // 编译所有子模块
                if (!paths.pwdIsRoot) {
                    return;
                }
                const cpaasRootConfig = paths.getCpaasConfig(paths.appPath);
                if (cpaasRootConfig && cpaasRootConfig.packages) {
                    buildPackageNames = cpaasRootConfig.packages
                        .filter((item) => !item.external)
                        .map((item) => item.name);
                }
                if (packages) {
                    buildPackageNames = buildPackageNames.filter((item) => packages.includes(item));
                }
            }
            // packages 下 找不到模块时
            if (buildPackageNames.length <= 0 || !buildPackageNames[0]) {
                if (!packages || packages.length <= 0 || !packages[0]) {
                    // console.error('请指定需要编译的子模块');
                    // process.exit(1);
                    return;
                }
                buildPackageNames = packages;
            }
            // concurrentBuildNumber 并发编译
            if (concurrentBuildNumber <= 1) {
                // 依次编译子模块
                for (const name of buildPackageNames) {
                    try {
                        yield new Promise((resolve, reject) => {
                            const args = [
                                // '--max_old_space_size=4196',
                                path.resolve(__dirname, './build-ms.bin.js'),
                            ];
                            const p = child_process.spawn('node', args, {
                                env: Object.assign(Object.assign({}, process.env), { BUILD_MODULE: name, CPAAS_APP_ROOT_PATH: paths.appRootPath }),
                                cwd: paths.getAppDirectory(name),
                                detached: false,
                                windowsHide: true,
                            });
                            p.stderr.pipe(process.stderr);
                            p.stdout.pipe(process.stdout);
                            p.on('close', (code) => {
                                p.kill();
                                if (code === 0) {
                                    resolve(null);
                                }
                                else {
                                    reject(code);
                                }
                            });
                        });
                    }
                    catch (e) {
                        console.error(e);
                        process.exit(1);
                    }
                }
            }
            else {
                const allTasks = buildPackageNames.map((name) => ({
                    name,
                    getPromise: () => __awaiter(this, void 0, void 0, function* () {
                        return yield new Promise((resolve, reject) => {
                            const args = [
                                // '--max_old_space_size=4196',
                                path.resolve(__dirname, './build-ms.bin.js'),
                            ];
                            const p = child_process.spawn('node', args, {
                                env: Object.assign(Object.assign({}, process.env), { BUILD_MODULE: name, CPAAS_APP_ROOT_PATH: paths.appRootPath }),
                                cwd: paths.getAppDirectory(name),
                                detached: false,
                                windowsHide: true,
                            });
                            p.stderr.pipe(process.stderr);
                            p.stdout.pipe(process.stdout);
                            p.on('close', (code) => {
                                p.kill();
                                if (code === 0) {
                                    resolve(null);
                                }
                                else {
                                    reject(code);
                                }
                            });
                        });
                    }),
                }));
                let execQueue = [];
                do {
                    if (allTasks.length > 0) {
                        for (let i = 0; i < concurrentBuildNumber &&
                            execQueue.length < concurrentBuildNumber; i++) {
                            const taskInfo = allTasks.shift();
                            if (taskInfo) {
                                const queueItem = {
                                    name: taskInfo.name,
                                    status: 'pending',
                                    promise: taskInfo.getPromise().then(() => {
                                        queueItem.status = 'resolve';
                                    }),
                                };
                                execQueue.push(queueItem);
                            }
                            else {
                                break;
                            }
                        }
                    }
                    yield Promise.race(execQueue.map((item) => item.promise));
                    execQueue = execQueue.filter((item) => item.status === 'pending');
                } while (execQueue.length > 0);
            }
        });
    }
    /**
     * 生成 external 模块配置文件
     */
    function refreshExternalMicroModuleConfig() {
        let microMapConfigPath = path.resolve(buildDistPath, 'packages/microConfig.json'); // 生成 dist/packages/microConfig.json
        const packagesDirPath = path.dirname(microMapConfigPath);
        if (!fs.existsSync(packagesDirPath)) {
            fs.mkdirpSync(packagesDirPath);
        }
        const cpaasRootConfig = paths.getCpaasConfig(paths.appPath);
        if (cpaasRootConfig &&
            cpaasRootConfig.packages &&
            cpaasRootConfig.packages.some((item) => item.external)) {
            let resultMicroConfigData = {};
            if (fs.existsSync(microMapConfigPath)) {
                let oldMicroMapConfig = fs.readJSONSync(microMapConfigPath);
                resultMicroConfigData = Object.assign(Object.assign({}, resultMicroConfigData), oldMicroMapConfig);
            }
            const microConfigData = cpaasRootConfig.packages
                .filter((item) => item.external)
                .reduce((result, item) => (Object.assign(Object.assign({}, result), { [item.name]: item })), {});
            resultMicroConfigData = Object.assign(Object.assign({}, resultMicroConfigData), microConfigData);
            fs.writeFileSync(microMapConfigPath, JSON.stringify(resultMicroConfigData));
        }
        if (!fs.existsSync(microMapConfigPath)) {
            fs.writeFileSync(microMapConfigPath, '{}');
        }
    }
    // We require that you explicitly set browsers and do not fall back to
    // browserslist defaults.
    const { checkBrowsers } = require('react-dev-utils/browsersHelper');
    try {
        yield checkBrowsers(paths.appRootPath, isInteractive);
        let previousFileSizes = null;
        try {
            previousFileSizes = yield measureFileSizesBeforeBuild(buildDistPath);
        }
        catch (e) {
            console.log(e);
        }
        // if (buildMicroWithoutParent) {
        //   console.log(chalk.yellow('build buildMicroWithoutParent...'));
        //   await _buildMicroWidthParent();
        //   return;
        // }
        const buildParent = () => __awaiter(void 0, void 0, void 0, function* () {
            // Remove all content but keep the directory so that
            // if you're in it, you don't end up in Trash
            fs.emptyDirSync(paths.appParentBuild);
            fs.readdirSync(paths.appRootBuild)
                .filter((item) => !['packages'].includes(item))
                .forEach((fileOrPath) => {
                const curPath = path.join(paths.appRootBuild, fileOrPath);
                if (fs.statSync(curPath).isDirectory()) {
                    // recurse
                    fs.readdirSync(curPath);
                }
                else {
                    // delete file
                    fs.unlinkSync(curPath);
                }
            });
            // Merge with the public folder
            if (!DISABLE_BUILD_DLL) {
                if (!fs.existsSync(paths.appDll)) {
                    yield require('./build-dll').default();
                }
            }
            // Start the webpack build
            const { stats, warnings, config } = (yield build(previousFileSizes));
            copyPublicFolder();
            refreshExternalMicroModuleConfig();
            try {
                const commitHash = yield git_utils_1.getHeadId();
                fs.writeFileSync(path.resolve(paths.appRootBuild, '.commitId'), commitHash);
            }
            catch (_a) { }
            if (!stats) {
                return;
            }
            if (warnings.length) {
                console.log(chalk.yellow('Compiled with warnings.\n'));
                console.log(warnings.join('\n\n'));
                console.log('\nSearch for the ' +
                    chalk.underline(chalk.yellow('keywords')) +
                    ' to learn more about each warning.');
                console.log('To ignore, add ' +
                    chalk.cyan('// eslint-disable-next-line') +
                    ' to the line before.\n');
            }
            else {
                console.log(chalk.green('Compiled successfully.\n'));
            }
            yield setCachedEnv(envData);
            console.log('File sizes after gzip:\n');
            if (previousFileSizes) {
                try {
                    printFileSizesAfterBuild(stats, previousFileSizes, buildDistPath, WARN_AFTER_BUNDLE_GZIP_SIZE, WARN_AFTER_CHUNK_GZIP_SIZE);
                }
                catch (e) {
                    console.error(e);
                }
            }
            console.log();
            const appPackage = require(paths.appPackageJson);
            const publicUrl = paths.publicUrl;
            const publicPath = config.output.publicPath;
            const buildFolder = path.relative(process.cwd(), paths.appParentBuild);
            printHostingInstructions(appPackage, publicUrl, publicPath, buildFolder, useYarn);
            try {
                fs.emptyDirSync(paths.appParentBuild);
                fs.rmdirSync(paths.appParentBuild, { recursive: true });
            }
            catch (ee) {
                console.error(ee);
            }
        });
        let isBuildParented = false;
        // 打包父模块
        if (!onlyBuildMicro && process.env.BUILD_SKIP_PARENT !== 'true') {
            yield buildParent();
            isBuildParented = true;
        }
        if (!isBuildParented &&
            fs.existsSync('.git/config') &&
            fs.existsSync(path.join(paths.appRootBuild, '.commitId')) &&
            SKIP_NO_CHANGE_MODULE) {
            const lastBuildCommitId = fs
                .readFileSync(path.join(paths.appRootBuild, '.commitId'))
                .toString();
            if (yield git_utils_1.fileHasChangeFromCommit(/^src\//, lastBuildCommitId)) {
                yield buildParent();
            }
            else {
                console.log('父工程无变化，跳过父工程编译');
            }
        }
        if (onlyBuildParent) {
            return;
        }
        if (!onlyBuildParent) {
            // @ts-ignore
            if (!packages || packages.length > 0) {
                yield buildCpaasMicroModules();
            }
        }
        if (onlyBuildMicro) {
            return;
        }
    }
    catch (errMsg) {
        console.log(chalk.red('Failed to compile.\n'));
        // printBuildError(errMsg);
        console.error(errMsg);
        process.exit(1);
    }
});
exports.default = bootstrap;
